#ifndef UHM_SOLVER_BASE_OPS_HXX_
#define UHM_SOLVER_BASE_OPS_HXX_

namespace uhm {
  template<class S_> 
  Int_ op_leaf_to_me_seq(S_ *s, typename S_::_Tree_::Tnd_ *me, 
                         Int_ (*op_func)(S_*, typename S_::_Tree_::Tnd_*));
  template<class S_>
  Int_ op_me_to_leaf_seq(S_ *s, typename S_::_Tree_::Tnd_ *me,
                         Int_ (*op_func)(S_*, typename S_::_Tree_::Tnd_*));
  template<class S_> 
  Int_ op_leaf_to_me_par(S_ *s, typename S_::_Tree_::Tnd_ *me, 
                         Int_ (*op_func)(S_*, typename S_::_Tree_::Tnd_*));
  template<class S_>
  Int_ op_me_to_leaf_par(S_ *s, typename S_::_Tree_::Tnd_ *me,
                         Int_ (*op_func)(S_*, typename S_::_Tree_::Tnd_*));
  
  // ** Operation
  template<class S_> 
  inline Int_ op_leaf_to_me_seq(S_ *s, typename S_::_Tree_::Tnd_ *me, 
                                Int_ (*op_func)(S_*, typename S_::_Tree_::Tnd_*)) {
    for (Int_ i=0;i<me->get_n_children();++i) {
      typename S_::_Tree_::Tnd_ *c = me->get_child(i);

      UHM_ERROR(op_leaf_to_me_seq<S_>(s, c, op_func), UHM_ERROR_SOLVER_OP_FUNCT);
    }
    UHM_ERROR(op_func(s, me), UHM_ERROR_SOLVER_OP_FUNCT);

    return true;
  }

  template<class S_>
  inline Int_ op_me_to_leaf_seq(S_ *s, typename S_::_Tree_::Tnd_ *me,
                                Int_ (*op_func)(S_*, typename S_::_Tree_::Tnd_*)) {
    UHM_ERROR(op_func(s, me), UHM_ERROR_SOLVER_OP_FUNCT);
    for (Int_ i=0;i<me->get_n_children();++i) {
      typename S_::_Tree_::Tnd_ *c = me->get_child(i);

      UHM_ERROR(op_me_to_leaf_seq<S_>(s, c, op_func), UHM_ERROR_SOLVER_OP_FUNCT);
    }

    return true;
  }

  // ** Multithreading
  template<class S_> 
  inline Int_ op_leaf_to_me_par(S_ *s, typename S_::_Tree_::Tnd_ *me, 
                                Int_ (*op_func)(S_*, typename S_::_Tree_::Tnd_*)) {
    for (Int_ i=0;i<me->get_n_children();++i) {
      typename S_::_Tree_::Tnd_ *c = me->get_child(i);

#pragma omp task firstprivate(c) 
      {
        UHM_ERROR(op_leaf_to_me_par<S_>(s, c, op_func), UHM_ERROR_SOLVER_OP_FUNCT);
      }
    }
#pragma omp taskwait

    UHM_ERROR(op_func(s, me), UHM_ERROR_SOLVER_OP_FUNCT);

    return true;
  }

  template<class S_>
  inline Int_ op_me_to_leaf_par(S_ *s, typename S_::_Tree_::Tnd_ *me,
                                Int_ (*op_func)(S_*, typename S_::_Tree_::Tnd_*)) {
    UHM_ERROR(op_func(s, me), UHM_ERROR_SOLVER_OP_FUNCT);

#pragma omp taskwait

    for (Int_ i=0;i<me->get_n_children();++i) {
      typename S_::_Tree_::Tnd_ *c = me->get_child(i);

#pragma omp task firstprivate(c) 
      {
        UHM_ERROR(op_me_to_leaf_par<S_>(s, c, op_func), UHM_ERROR_SOLVER_OP_FUNCT);
      }
    }

    return true;
  }

}

#endif
